/*
 *  NFCSigning - Open source library for signing/validation of NDEF messages
 *  Copyright (C) 2009-2010 The NFCSigning Team
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 */
package org.nfcsigning;

import org.nfcsigning.MessageToSign.Mark;
import org.nfcsigning.algorithm.DefaultVerifierFactory;
import org.nfcsigning.algorithm.PrivateKey;
import org.nfcsigning.algorithm.RSASSAPSSSigner;
import org.nfcsigning.algorithm.SignatureException;
import org.nfcsigning.algorithm.SignatureVerifier;
import org.nfcsigning.algorithm.Signer;
import org.nfcsigning.certificate.CertificateChain;
import org.nfcsigning.certificate.DefaultCertificateChainFactory;
import org.nfcsigning.certificate.X509Certificate;
import org.nfcsigning.certificate.X509CertificateChain;
import org.nfcsigning.record.CertificateBytes;
import org.nfcsigning.record.CertificateField;
import org.nfcsigning.record.InvalidSignatureRecord;
import org.nfcsigning.record.SignatureField;
import org.nfcsigning.record.SignatureRecord;
import org.nfcsigning.record.URIRecord;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Vector;
import jmunit.framework.cldc11.AssertionFailedException;
import jmunit.framework.cldc11.TestCase;
import org.bouncycastle.crypto.DataLengthException;

//#ifdef MyJSR257
//# import my.javax.microedition.contactless.ndef.NDEFMessage;
//# import my.javax.microedition.contactless.ndef.NDEFRecord;
//# import my.javax.microedition.contactless.ndef.NDEFRecordType;
//#else
import javax.microedition.contactless.ndef.NDEFMessage;
import javax.microedition.contactless.ndef.NDEFRecord;
import javax.microedition.contactless.ndef.NDEFRecordType;
//#endif

/**
 * APITestCase.java
 * JMUnit based test
 *
 * Created on 2008-sep-26, 16:07:47
 *
 * @author Markus Kilås
 */
public class APITestCase extends TestCase {
    
    private TestData testData = new TestData();
    
    public APITestCase() {
        // The first parameter of inherited constructor is the number of test cases
        super(23, "APITestCase");
    }            

    public void test(int testNumber) throws Throwable {
        
        System.out.println("\n\n------ T E S T  " + testNumber + "  -------");
        
        switch (testNumber) {
            case 0:
                test1();
                break;
            case 1:
                testSplitIntoRangesMap();
                break;
            case 2:
                testSampleData();
                break;
            case 3:
                testToCanonicalizedFormFromOneRecord();
                break;
            case 4:
                testToCanonicalizedFormFromMultipleRecords();
                break;
            case 5:
                testPutTogetherRecords();
                break;
            case 6:
                testSignWithPSS();
                break;
            case 7:
                testValidateFromData1();
                break;
            case 8:
                testSignAndValidate();
                break;
            case 9:
                testChainFetcher();
                break;
            case 10:
                testSignatureRanges();
                break;
            case 11:
                testSignWithOneU();
                break;
            case 12:
                testSignatureFetcher();
                break;
            case 13:
                testValidateFromDataPKCS1();
                break;
            case 14:
                testValidateFromDataECDSA();
                break;
            case 15:
                testValidateFromDataDSA();
                break;
            case 16:
                testValidateFromDataDSAAllIndexes();
                break;
            case 17:
                testSignWithPSSExternal();
                break;
            case 18:
                testSignAndValidateExternalType();
                break;
            case 19:
                testSignAndValidateOtherSigAlg();
                break;
            case 20:
                testSignAndValidateOtherCertFormat();
                break;
            case 21:
                testValidateFromData2();
                break;
            case 22:
                testValidateFromData3();
                break;
            case 23:

                break;
            case 24:
                
                break;
            case 25:
                
                break;
            default:
                break;
        }
    }

    

    
    /**
     * Dummy test
     */
    public void test1() throws AssertionFailedException {
        System.out.println("test1");
        
        Canonicalizer canonicalizer = null;
    }

    private SignatureRecord dummySignatureRecord() {
        return new SignatureRecord(new SignatureField(SignatureField.RSASSA_PSS, new byte[]{0xA}), new CertificateField(CertificateField.X509, new CertificateBytes[0]));
    }

    private void testSplitIntoRangesMap() throws AssertionFailedException {

        System.out.println("testSplitIntoRangesMap");

        URIRecord uri1 = new URIRecord("http://www.url.com");
        URIRecord uri2 = new URIRecord("http://www.url.se");
        URIRecord uri3 = new URIRecord("http://www.url.se");
        URIRecord uri4 = new URIRecord("ftp://www.url.com");

        SignatureRecord mark1 = SignatureRecord.createMark();
        SignatureRecord sig1 = new SignatureRecord(new SignatureField(SignatureField.RSASSA_PSS, new byte[]{0xA}), new CertificateField(CertificateField.X509, new CertificateBytes[0]));

        // Map 1: {U,Sig} 
        {
            NDEFRecord[] rs1 = new NDEFRecord[] {
                uri1.toNDEFRecord(), 
                sig1.toNDEFRecord()
            };
            Hashtable map1 = new MessageValidator(null).splitIntoRangesMap(rs1);

            Object o = map1.get(new Integer(0));
            assertNotNull("map1 record 0", o);

            SignedRange range = (SignedRange) o;
            assertEquals("map1 record 0 sig", sig1, range.getSignatureRecord());
            assertEquals("map1 index 0", 0, range.getRecordIndexes()[0]);
            assertEquals("map1 indexes length", 1, range.getRecordIndexes().length);

            o = map1.get(new Integer(1));
            assertNull("map1 record 1", o);
        }

        // Map 2: {U,M,U,Sig}
        {
            NDEFRecord[] rs2 = new NDEFRecord[] {
                uri1.toNDEFRecord(), 
                mark1.toNDEFRecord(),
                uri2.toNDEFRecord(),
                sig1.toNDEFRecord()
            };
            Hashtable map2 = new MessageValidator(null).splitIntoRangesMap(rs2);

            Object o = (SignedRange) map2.get(new Integer(0));
            assertNull("map2 record 0", o);

            o = (SignedRange) map2.get(new Integer(1));
            assertNull("map2 record 1", o);

            o = (SignedRange) map2.get(new Integer(2));
            assertNotNull("map2 record 2", o);

            SignedRange range = (SignedRange) o;
            assertEquals("map2 record 1 sig", sig1, range.getSignatureRecord());
            assertEquals("map2 index 2", 2, range.getRecordIndexes()[0]);
            assertEquals("map2 indexes length", 1, range.getRecordIndexes().length);

            o = (SignedRange) map2.get(new Integer(3));
            assertNull("map2 record 3", o);
        }

        // Map 3: {U,M,U,U,Sig}
        {
            NDEFRecord[] rs2 = new NDEFRecord[] {
                uri1.toNDEFRecord(), 
                mark1.toNDEFRecord(),
                uri2.toNDEFRecord(),
                uri3.toNDEFRecord(),
                sig1.toNDEFRecord()
            };
            Hashtable map2 = new MessageValidator(null).splitIntoRangesMap(rs2);

            Object o = (SignedRange) map2.get(new Integer(0));
            assertNull("map3 record 0", o);

            o = (SignedRange) map2.get(new Integer(1));
            assertNull("map3 record 1", o);

            o = (SignedRange) map2.get(new Integer(2));
            assertNotNull("map3 record 2", o);

            SignedRange range = (SignedRange) o;
            assertEquals("map3 record 1 sig", sig1, range.getSignatureRecord());
            assertEquals("map3 index 2", 2, range.getRecordIndexes()[0]);
            assertEquals("map3 indexes length", 2, range.getRecordIndexes().length);

            assertEquals("map3 record 2 sig", range, map2.get(new Integer(3)));

            o = (SignedRange) map2.get(new Integer(4));
            assertNull("map3 record 4", o);
        }

        // Map 4: {U,M,U,U,ExtSig}
        {
            final NDEFRecordType externalType = new NDEFRecordType(NDEFRecordType.EXTERNAL_RTD, "urn:nfc:ext:ericsson.com:ExternalSig");

            NDEFRecord[] rs2 = new NDEFRecord[] {
                uri1.toNDEFRecord(),
                mark1.toNDEFRecord(),
                uri2.toNDEFRecord(),
                uri3.toNDEFRecord(),
                sig1.toNDEFRecord(externalType, new byte[0])
            };
            Hashtable map2 = new MessageValidator(null) {

                protected boolean isSignatureRecord(NDEFRecordType recordType) {

                    if(externalType.equals(recordType)) {
                        return true;
                    }

                    return super.isSignatureRecord(recordType);
                }

            }.splitIntoRangesMap(rs2);

            Object o = (SignedRange) map2.get(new Integer(0));
            assertNull("map4 record 0", o);

            o = (SignedRange) map2.get(new Integer(1));
            assertNull("map4 record 1", o);

            o = (SignedRange) map2.get(new Integer(2));
            assertNotNull("map4 record 2", o);

            SignedRange range = (SignedRange) o;
            assertEquals("map4 record 1 sig", sig1, range.getSignatureRecord());
            assertEquals("map4 index 2", 2, range.getRecordIndexes()[0]);
            assertEquals("map4 indexes length", 2, range.getRecordIndexes().length);

            assertEquals("map4 record 2 sig", range, map2.get(new Integer(3)));

            o = (SignedRange) map2.get(new Integer(4));
            assertNull("map4 record 4", o);
        }

        // TODO: Add more ranges
    }
    
    private void testSampleData() throws AssertionFailedException {
        System.out.println("testSampleData");
        
        StringBuffer sb = new StringBuffer(280);
        sb.append("http://www.ericsson.com/?id=");
        for(int i = 0; i < 255; i++) {
            sb.append(i%10);
        }
        
        URIRecord longRec = new URIRecord(sb.toString());
//        System.out.println("U: " + longRec.getURI());
        
//        Utils.dumpAsJavaByteArray(longRec.toNDEFRecord().toByteArray());
        
        testSampleData("recordU1_SR0_IL1", TestDataOld.recordU1_SR0_IL1, "http://www.ericsson.com");
        testSampleData("recordU1_SR1_IL1", TestDataOld.recordU1_SR1_IL1, "http://www.ericsson.com");
        testSampleData("recordU1_SR0_IL0", TestDataOld.recordU1_SR0_IL0, "http://www.ericsson.com");
        testSampleData("recordU1_SR1_IL0", TestDataOld.recordU1_SR1_IL0, "http://www.ericsson.com");
        testSampleData("recordU2_SR0_IL1", TestDataOld.recordU2_SR0_IL1, "http://www.ericsson.com/?id=012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234");
    }
    
    private void testSampleData(String title, byte[] data, String sUri) throws AssertionFailedException {
        try {
            NDEFMessage msg = new NDEFMessage(data, 0);
            
            assertEquals(title + ": type", URIRecord.RECORD_TYPE, msg.getRecord(0).getRecordType());
            
            URIRecord uri = new URIRecord(msg.getRecord(0).getPayload(), 0);
            assertEquals(title + ": URL", sUri, uri.getURI());
        } catch(Throwable t) {
            t.printStackTrace();
            fail("title: " + t);
        }
    }
    
    private void testToCanonicalizedFormFromOneRecord() throws AssertionFailedException {
        System.out.println("testToCanonicalizedFormFromOneRecord");
        
        {
            byte[] canonicalized = TestDataOld.recordU1_canonicalized;       
            byte[][] data = new byte[][]{
                TestDataOld.recordU1_SR1_IL1,
                TestDataOld.recordU1_SR1_IL0,        
                TestDataOld.recordU1_SR0_IL1,
                TestDataOld.recordU1_SR0_IL0,
            };
            for(int i = 0; i < data.length; i++) {
                System.out.println("test " + i);
                NDEFRecord record = new NDEFRecord(data[i], 0);
                assertTrue(" test "+i+" :", Utils.equals(canonicalized, 
                        Canonicalizer.toCanonicalizedForm(record)));
            }
        }
        
        {
            byte[] canonicalized = TestDataOld.recordU2_canonicalized;       
            byte[][] data = new byte[][]{
                TestDataOld.recordU2_SR0_IL1,
                TestDataOld.recordU2_SR0_IL0
            };
            for(int i = 0; i < data.length; i++) {
                System.out.println("test " + i);
                NDEFRecord record = new NDEFRecord(data[i], 0);
                assertTrue(" test2 "+i+" :", Utils.equals(canonicalized, 
                        Canonicalizer.toCanonicalizedForm(record)));
            }
        }
        
    }
    
    private void testToCanonicalizedFormFromMultipleRecords() throws AssertionFailedException {
        System.out.println("testToCanonicalizedFormFromMultipleRecords");
        
        
        // One record
        {
            byte[] expected = TestDataOld.recordU1_canonicalized;       
            byte[] data = TestDataOld.recordU1_SR0_IL0;
            NDEFRecord record = new NDEFRecord(data, 0);
            
            assertTrue("one 1", Utils.equals(expected, Canonicalizer.toCanonicalizedForm(record)));
        }
        
        // Two records
        {
            NDEFRecord[] records = new NDEFRecord[] {
                new NDEFRecord(TestDataOld.recordU1_SR0_IL0, 0),
                new NDEFRecord(TestDataOld.recordU1_SR0_IL1, 0),
            };
            byte[] rec1 = Canonicalizer.toCanonicalizedForm(new NDEFRecord(TestDataOld.recordU1_SR0_IL0, 0));
            byte[] rec2 = Canonicalizer.toCanonicalizedForm(new NDEFRecord(TestDataOld.recordU1_SR0_IL1, 0));
            
            byte[] expected = new byte[rec1.length+rec2.length];
            System.arraycopy(rec1, 0, expected, 0, rec1.length);
            System.arraycopy(rec2, 0, expected, rec1.length, rec2.length);
            
            // Handle MB and ME
            //expected[0] |= 0x80; // MB=1
            //expected[0] &= 0xBF; // ME=0
            //expected[rec1.length] &= 0x7F; // MB=0
            //expected[rec1.length] |= 0x40; // ME=1
            
            expected[0] |= 0x80; // MB=1
            expected[0] |= 0x40; // ME=1
            expected[rec1.length] |= 0x80; // MB=1
            expected[rec1.length] |= 0x40; // ME=1
            
//            System.out.println("Expected: ");
//            com.ericsson.nfc.signing.certs.Utils.dumpAsJavaByteArray(expected, 1);
            
            assertTrue("two 1", Utils.equals(expected, Canonicalizer.toCanonicalizedForm(records, new int[]{0,1})));
        }
        
        // Three records, all signed
        {
            NDEFRecord[] records = new NDEFRecord[] {
                new NDEFRecord(TestDataOld.recordU1_SR0_IL0, 0),
                new NDEFRecord(TestDataOld.recordU1_SR1_IL0, 0),
                new NDEFRecord(TestDataOld.recordU1_SR0_IL1, 0),
            };
            byte[] rec1 = Canonicalizer.toCanonicalizedForm(new NDEFRecord(TestDataOld.recordU1_SR0_IL0, 0));
            byte[] rec2 = Canonicalizer.toCanonicalizedForm(new NDEFRecord(TestDataOld.recordU1_SR1_IL0, 0));
            byte[] rec3 = Canonicalizer.toCanonicalizedForm(new NDEFRecord(TestDataOld.recordU1_SR0_IL1, 0));
            
            byte[] expected = new byte[rec1.length+rec2.length+rec3.length];
            System.arraycopy(rec1, 0, expected, 0, rec1.length);
            System.arraycopy(rec2, 0, expected, rec1.length, rec2.length);
            System.arraycopy(rec3, 0, expected, rec1.length+rec2.length, rec3.length);
            
            // Handle MB and ME
            //expected[0] |= 0x80; // MB=1
            //expected[0] &= 0xBF; // ME=0
            //expected[rec1.length+rec2.length] &= 0x7F; // MB=0
            //expected[rec1.length+rec2.length] |= 0x40; // ME=1
            
            expected[0] |= 0x80; // MB=1
            expected[0] |= 0x40; // ME=1
            expected[rec1.length+rec2.length] |= 0x80; // MB=1
            expected[rec1.length+rec2.length] |= 0x40; // ME=1
            
//            System.out.println("Expected: ");
//            com.ericsson.nfc.signing.certs.Utils.dumpAsJavaByteArray(expected, 1);
            
            assertTrue("three 1", Utils.equals(expected, Canonicalizer.toCanonicalizedForm(records, new int[]{0,1,2})));
        }
        
        // Three records, index 0,1 signed
        {
            NDEFRecord[] records = new NDEFRecord[] {
                new NDEFRecord(TestDataOld.recordU1_SR0_IL0, 0),
                new NDEFRecord(TestDataOld.recordU1_SR1_IL0, 0),
                new NDEFRecord(TestDataOld.recordU1_SR0_IL1, 0),
            };
            byte[] rec1 = Canonicalizer.toCanonicalizedForm(new NDEFRecord(TestDataOld.recordU1_SR0_IL0, 0));
            byte[] rec2 = Canonicalizer.toCanonicalizedForm(new NDEFRecord(TestDataOld.recordU1_SR1_IL0, 0));
            
            byte[] expected = new byte[rec1.length+rec2.length/*+rec3.length*/];
            System.arraycopy(rec1, 0, expected, 0, rec1.length);
            System.arraycopy(rec2, 0, expected, rec1.length, rec2.length);

            // Handle MB and ME
            //expected[0] |= 0x80; // MB=1
            //expected[0] &= 0xBF; // ME=0
//          //expected[rec1.length+rec2.length] &= 0x7F; // MB=0
//          //expected[rec1.length+rec2.length] |= 0x40; // ME=1
            
            expected[0] |= 0x80; // MB=1
            expected[0] |= 0x40; // ME=1
            expected[rec1.length] |= 0x80; // MB=1
            expected[rec1.length] |= 0x40; // ME=1
            
//            System.out.println("Expected: ");
//            com.ericsson.nfc.signing.certs.Utils.dumpAsJavaByteArray(expected, 1);
            
            assertTrue("three 2", Utils.equals(expected, Canonicalizer.toCanonicalizedForm(records, new int[]{0,1})));
        }
        
        // Three records, index 1,2 signed
        {
            NDEFRecord[] records = new NDEFRecord[] {
                new NDEFRecord(TestDataOld.recordU1_SR0_IL0, 0),
                new NDEFRecord(TestDataOld.recordU1_SR1_IL0, 0),
                new NDEFRecord(TestDataOld.recordU1_SR0_IL1, 0),
            };
            byte[] rec2 = Canonicalizer.toCanonicalizedForm(new NDEFRecord(TestDataOld.recordU1_SR1_IL0, 0));
            byte[] rec3 = Canonicalizer.toCanonicalizedForm(new NDEFRecord(TestDataOld.recordU1_SR0_IL1, 0));
            
            byte[] expected = new byte[rec2.length+rec3.length];
            System.arraycopy(rec2, 0, expected, 0, rec2.length);
            System.arraycopy(rec3, 0, expected, rec2.length, rec3.length);
            
            // Handle MB and ME
//          //expected[0] |= 0x80; // MB=1
//          //expected[0] &= 0xBF; // ME=0
            //expected[rec2.length] &= 0x7F; // MB=0
            //expected[rec2.length] |= 0x40; // ME=1
            
            expected[0] |= 0x80; // MB=1
            expected[0] |= 0x40; // ME=1
            expected[rec2.length] |= 0x80; // MB=1
            expected[rec2.length] |= 0x40; // ME=1
            
//            System.out.println("Expected: ");
//            com.ericsson.nfc.signing.certs.Utils.dumpAsJavaByteArray(expected, 1);
            
            assertTrue("three 3", Utils.equals(expected, Canonicalizer.toCanonicalizedForm(records, new int[]{1,2})));
        }
    }
    
    
    
    
    
    
    
    private void testPutTogetherRecords() throws AssertionFailedException {
        
        System.out.println("testPutTogetherRecords");

        URIRecord uri1 = new URIRecord("http://www.ericsson.com/1");
        URIRecord uri2 = new URIRecord("http://www.ericsson.com/2");
        URIRecord uri3 = new URIRecord("http://www.ericsson.com/3");

        // {U,U,U} => {U,SigM,U,U,Sig}
        {
            NDEFRecord[] records1 = new NDEFRecord[]{uri1.toNDEFRecord(), uri2.toNDEFRecord(), uri3.toNDEFRecord()};

            Vector v1 = new Vector();
            SignedRange range1 = new SignedRange();
            range1.add(new Integer(1));
            range1.add(new Integer(2));
            range1.setSignatureRecord(dummySignatureRecord());

            Signature sig = new Signature(dummySignatureRecord());
            Signature[] sigs = new Signature[] { null, sig, sig };

            v1.addElement(range1);
            Vector result1 = MessageToSign.putTogetherRecords(records1, sigs);
            assertEquals("v1 element 0", URIRecord.RECORD_TYPE, ((NDEFRecord)result1.elementAt(0)).getRecordType());
            assertTrue("v1 element 1", result1.elementAt(1) instanceof Mark);
            assertEquals("v1 element 2", URIRecord.RECORD_TYPE, ((NDEFRecord)result1.elementAt(2)).getRecordType());
            assertEquals("v1 element 3", URIRecord.RECORD_TYPE, ((NDEFRecord)result1.elementAt(3)).getRecordType());
            assertFalse("v1 element 4", ((Signature)result1.elementAt(4)).getSignatureRecord().isMarker());
        }
        
        // {U} => {U,Sig}
        {
            NDEFRecord[] records2 = new NDEFRecord[]{uri1.toNDEFRecord()};

            Vector v1 = new Vector();
            SignedRange range1 = new SignedRange();
            range1.add(new Integer(0));
            range1.setSignatureRecord(dummySignatureRecord());

            Signature sig = new Signature(dummySignatureRecord());
            Signature[] sigs = new Signature[] { sig };

            v1.addElement(range1);
            Vector result1 = MessageToSign.putTogetherRecords(records2, sigs);
            assertEquals("v1 element 0", URIRecord.RECORD_TYPE, ((NDEFRecord)result1.elementAt(0)).getRecordType());
            assertFalse("v1 element 1", ((Signature)result1.elementAt(1)).getSignatureRecord().isMarker());
        }
    }
    private static final NDEFRecordType smartPosterRecordType = new NDEFRecordType(NDEFRecordType.NFC_FORUM_RTD, "urn:nfc:wkt:Sp");
    
    static class EmptyChainFetcher implements CertificateFetcher {

        public CertificateChain fetchChain(URIRecord chainURI) {
            return new X509CertificateChain(new X509Certificate[0]);
        }
        
    }
    
    private void testValidateFromData1() throws AssertionFailedException {
        try {
            System.out.println("testValidateFromData1");

            NDEFRecord smartPosterRecord = new NDEFRecord(smartPosterRecordType, new byte[0], TestDataOld.msgSpUrlSig1);

            byte[] messageOnTag = smartPosterRecord.toByteArray();
//            Utils.dumpAsJavaByteArray(messageOnTag);

            X509Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream("files/SwedenRootCA.cer"))};
            NDEFMessage msg = new NDEFMessage(messageOnTag, 0);
            NDEFRecord r = msg.getRecord(0);

            System.out.println("record type: " + r.getRecordType().getName());

            MessageValidator validator = new MessageValidator(trustedCerts, null, new EmptyChainFetcher());
            SignedMessage signedMessage = validator.validate(r.getNestedNDEFMessage(0), new int[]{0, 1});

            Signature signature0 = signedMessage.getRecordsSignature(0);
            System.out.println("signature0: "+signature0);
            
            Signature signature1 = signedMessage.getRecordsSignature(1);
            System.out.println("signature1: "+signature1);
            
            assertNull("record 0 not valid", signature0);
            assertNull("record 1 not valid", signature1);
            
        } catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
    }
    
    private void testValidateFromDataPKCS1() throws AssertionFailedException {
        try { 
            System.out.println("testValidateFromDataPKCS1");
            
//            NDEFRecord smartPosterRecord = new NDEFRecord(smartPosterRecordType, new byte[0], msgSpUrlSigPKCS1);
            
            //NDEFMessage msg = new NDEFMessage(new NDEFRecord[]{smartPosterRecord});
            NDEFMessage msg = new NDEFMessage(TestDataOld.msgSpUrlSigPKCS1, 0);

            NDEFRecord r = msg.getRecord(0);
            System.out.println(r.getRecordType().getName());

            NDEFMessage sp = r.getNestedNDEFMessage(0);
            
            System.out.println("num " + sp.getNumberOfRecords());
            NDEFRecord r0 = sp.getRecord(0);
            System.out.println("r0: " + r0.getRecordType().getName());
            NDEFRecord r1 = sp.getRecord(1);

            X509Certificate[] trustedCerts = new X509Certificate[2];
            trustedCerts[0] = new X509Certificate(getClass().getResourceAsStream("files/markus2-pub.cer"));
            trustedCerts[1] = new X509Certificate(getClass().getResourceAsStream("files/SwedenRootCA.cer"));
            
            MessageValidator validator = new MessageValidator(trustedCerts, null, new CertificateFetcher(){

                public CertificateChain fetchChain(URIRecord chainURI) {
                    try {
                        String uri = chainURI.getURI();
                        System.out.println("Fetching for URI " + chainURI.getURI());

                        if("sampleappa:certificate1".equals(uri)) {
                            X509Certificate[] chain = new X509Certificate[1];
                            chain[0] = new X509Certificate(APITestCase.this.getClass().getResourceAsStream("files/AndersB-pub.cer"));
                            return new X509CertificateChain(chain);
                        }

                    } catch (IOException ex) {
                        ex.printStackTrace();
                    }
                    return null;
                }
            });

            SignedMessage result = validator.validate(sp, new int[]{0});

            Signature signature = result.getRecordsSignature(0);
            
            URIRecord uri = new URIRecord(sp.getRecords()[0].getPayload(), 0);

            assertEquals("http://www.2", uri.getURI());
            
            if(signature == null) {
                fail("Invalid signature");
            } else if(!signature.getStatus().isValid()) {
                fail("Invalid signature, " + signature.getStatus().getErrorMessage());
            } else {
                if(signature.getStatus().getCertificateChain().getUserCertificate() instanceof X509Certificate) {
                    System.out.println("Valid signature from " + ((X509Certificate)signature.getStatus().getCertificateChain().getUserCertificate()).getStructure().getSubject());
                }
            }
            
        } catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
    }
    
    private void testValidateFromDataECDSA() throws AssertionFailedException {
        try { 
            System.out.println("testValidateFromDataECDSA");
            
//            NDEFRecord smartPosterRecord = new NDEFRecord(smartPosterRecordType, new byte[0], msgSpUrlSigPKCS1);
            
            //NDEFMessage msg = new NDEFMessage(new NDEFRecord[]{smartPosterRecord});
            NDEFMessage msg = new NDEFMessage(testData.getBytes(TestData.msgSpUrlMoraNisseChainSigECDSA), 0);

            NDEFRecord r = msg.getRecord(0);
            System.out.println(r.getRecordType().getName());

            NDEFMessage sp = r.getNestedNDEFMessage(0);
            
            System.out.println("num " + sp.getNumberOfRecords());
            NDEFRecord r0 = sp.getRecord(0);
            System.out.println("r0: " + r0.getRecordType().getName());
            NDEFRecord r1 = sp.getRecord(1);

            X509Certificate[] trustedCerts = new X509Certificate[2];
            trustedCerts[0] = new X509Certificate(getClass().getResourceAsStream(Files.NORDIC_ROOT_CA));
            trustedCerts[1] = new X509Certificate(getClass().getResourceAsStream(Files.SWEDEN_ROOT_CA));
            
            MessageValidator validator = new MessageValidator(trustedCerts, null, null);

            SignedMessage result = validator.validate(sp, new int[]{0});

            Signature signature = result.getRecordsSignature(0);
            
            URIRecord uri = new URIRecord(sp.getRecords()[0].getPayload(), 0);

            assertEquals("http://www.ericsson.com", uri.getURI());
            
            if(signature == null) {
                fail("Invalid signature");
            } else if(!signature.getStatus().isValid()) {
                fail("Invalid signature, " + signature.getStatus().getErrorMessage());
            } else {
                System.out.println("Valid signature from " + ((X509Certificate)signature.getStatus().getCertificateChain().getUserCertificate()).getStructure().getSubject());
            }
            
        } catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
    }
    
    private void testValidateFromDataDSA() throws AssertionFailedException {
        try { 
            System.out.println("testValidateFromDataDSA");
            
//            NDEFRecord smartPosterRecord = new NDEFRecord(smartPosterRecordType, new byte[0], msgSpUrlSigPKCS1);
            
            //NDEFMessage msg = new NDEFMessage(new NDEFRecord[]{smartPosterRecord});
            
            
            
            NDEFMessage msg = new NDEFMessage(testData.getBytes(TestData.msgSpUrlLarsNilsChainSigDSA), 0);

            NDEFRecord r = msg.getRecord(0);
            System.out.println(r.getRecordType().getName());

            NDEFMessage sp = r.getNestedNDEFMessage(0);
            
            System.out.println("num " + sp.getNumberOfRecords());
            NDEFRecord r0 = sp.getRecord(0);
            System.out.println("r0: " + r0.getRecordType().getName());
            NDEFRecord r1 = sp.getRecord(1);

            X509Certificate[] trustedCerts = new X509Certificate[3];
            trustedCerts[0] = new X509Certificate(getClass().getResourceAsStream(Files.SCANDINAVIA_ROOT_CA));
            trustedCerts[1] = new X509Certificate(getClass().getResourceAsStream(Files.SWEDEN_ROOT_CA));
            trustedCerts[2] = new X509Certificate(getClass().getResourceAsStream(Files.NORDIC_ROOT_CA));
            
            MessageValidator validator = new MessageValidator(trustedCerts, null, null);

            SignedMessage result = validator.validate(sp, new int[]{0});

            Signature signature = result.getRecordsSignature(0);
            
            URIRecord uri = new URIRecord(sp.getRecords()[0].getPayload(), 0);

            assertEquals("http://www.dsa.se", uri.getURI());
            
            if(signature == null) {
                fail("Invalid signature");
            } else if(!signature.getStatus().isValid()) {
                fail("Invalid signature, " + signature.getStatus().getErrorMessage());
            } else {
                System.out.println("Valid signature from " + ((X509Certificate)signature.getStatus().getCertificateChain().getUserCertificate()).getStructure().getSubject());
            }
            
        } catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
    }

    private void testValidateFromDataDSAAllIndexes() throws AssertionFailedException {
        try {
            System.out.println("testValidateFromDataDSAAllIndexes");

//            NDEFRecord smartPosterRecord = new NDEFRecord(smartPosterRecordType, new byte[0], msgSpUrlSigPKCS1);

            //NDEFMessage msg = new NDEFMessage(new NDEFRecord[]{smartPosterRecord});



            NDEFMessage msg = new NDEFMessage(testData.getBytes(TestData.msgSpUrlLarsNilsChainSigDSA), 0);

            NDEFRecord r = msg.getRecord(0);
            System.out.println(r.getRecordType().getName());

            NDEFMessage sp = r.getNestedNDEFMessage(0);

            System.out.println("num " + sp.getNumberOfRecords());
            NDEFRecord r0 = sp.getRecord(0);
            System.out.println("r0: " + r0.getRecordType().getName());
            NDEFRecord r1 = sp.getRecord(1);

            X509Certificate[] trustedCerts = new X509Certificate[3];
            trustedCerts[0] = new X509Certificate(getClass().getResourceAsStream(Files.SCANDINAVIA_ROOT_CA));
            trustedCerts[1] = new X509Certificate(getClass().getResourceAsStream(Files.SWEDEN_ROOT_CA));
            trustedCerts[2] = new X509Certificate(getClass().getResourceAsStream(Files.NORDIC_ROOT_CA));

            MessageValidator validator = new MessageValidator(trustedCerts, null, null);

            SignedMessage result = validator.validate(sp); // All indexes

            Signature signature = result.getRecordsSignature(0);

            URIRecord uri = new URIRecord(sp.getRecords()[0].getPayload(), 0);

            assertEquals("http://www.dsa.se", uri.getURI());

            if(signature == null) {
                fail("Invalid signature");
            } else if(!signature.getStatus().isValid()) {
                fail("Invalid signature, " + signature.getStatus().getErrorMessage());
            } else {
                System.out.println("Valid signature from " + ((X509Certificate)signature.getStatus().getCertificateChain().getUserCertificate()).getStructure().getSubject());
            }

        } catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
    }
    
    private void testSignWithPSS() throws AssertionFailedException {
        try {
            System.out.println("testSignWithPSS");

            URIRecord uri1 = new URIRecord(new byte[]{(byte) 1}, "http://www.ericsson.com/1");
            URIRecord uri2 = new URIRecord(new byte[]{(byte) 2}, "http://www.ericsson.com/2");

            NDEFRecord[] records = new NDEFRecord[]{uri1.toNDEFRecord(), uri2.toNDEFRecord()};
            NDEFMessage payload = new NDEFMessage(records);
            PrivateKey privKey = new PrivateKey(getClass().getResourceAsStream("files/KalleKarlsson-priv.der"));
            CertificateBytes[] chain = new CertificateBytes[]{new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/KalleKarlsson.cer")).getBytes()), new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/TrainCompanyCA.cer")).getBytes()), new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer")).getBytes())};

            Signer signer = new RSASSAPSSSigner(privKey, CertificateField.X509, chain, null);
            MessageToSign toSign = new MessageToSign(payload);
            toSign.setSigner(1, signer);
            SignedMessage signed = toSign.signMessage();
            NDEFMessage signedPayload = signed.toNDEFMessage();
            
            System.out.println("records: "+signedPayload.getNumberOfRecords());
            
            try {
                System.out.println("rec0: " + signedPayload.getRecord(0).getRecordType().getName());
                System.out.println("rec1: " + signedPayload.getRecord(1).getRecordType().getName());
                System.out.println("rec2: " + signedPayload.getRecord(2).getRecordType().getName());
                System.out.println("rec3: " + signedPayload.getRecord(3).getRecordType().getName());
                
                assertEquals("rec0=U", URIRecord.RECORD_TYPE, signedPayload.getRecord(0).getRecordType());
                assertEquals("rec1=Sig", SignatureRecord.RECORD_TYPE, signedPayload.getRecord(1).getRecordType());
                assertTrue("rec1=marker", new SignatureRecord(signedPayload.getRecord(1)).isMarker());
                assertEquals("rec2=U", URIRecord.RECORD_TYPE, signedPayload.getRecord(2).getRecordType());
                assertEquals("rec3=Sig", SignatureRecord.RECORD_TYPE, signedPayload.getRecord(3).getRecordType());
                assertFalse("rec3=not marker", new SignatureRecord(signedPayload.getRecord(3)).isMarker());
                
            } catch (Throwable ex) {
                ex.printStackTrace();
                fail("SigRec-fail:" + ex);
            }
            
        } catch (SignatureException ex) {
            ex.printStackTrace();
            fail("SignException: " + ex.getMessage());
        } catch (DataLengthException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
    }
    
    public void testSignWithOneU() throws AssertionFailedException {
        try {
            System.out.println("testSignWithOneU");

            URIRecord uri1 = new URIRecord(new byte[]{(byte) 1}, "http://www.ericsson.com/one");

            NDEFRecord[] records = new NDEFRecord[]{uri1.toNDEFRecord()};
            NDEFMessage payload = new NDEFMessage(records);
            PrivateKey privKey = new PrivateKey(getClass().getResourceAsStream("files/KalleKarlsson-priv.der"));
            CertificateBytes[] chain = new CertificateBytes[]{new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/KalleKarlsson.cer")).getBytes()), new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/TrainCompanyCA.cer")).getBytes()), new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer")).getBytes())};

            Signer signer = new RSASSAPSSSigner(privKey, CertificateField.X509, chain, null);
            MessageToSign toSign = new MessageToSign(payload);
            toSign.setSigner(0, signer);
            SignedMessage signed = toSign.signMessage();
            NDEFMessage signedPayload = signed.toNDEFMessage();
            
            System.out.println("records: "+signedPayload.getNumberOfRecords());
            
            System.out.println("rec0: " + signedPayload.getRecord(0).getRecordType().getName());
            System.out.println("rec1: " + signedPayload.getRecord(1).getRecordType().getName());

            assertEquals("rec0=U", URIRecord.RECORD_TYPE, signedPayload.getRecord(0).getRecordType());
            assertEquals("rec1=Sig", SignatureRecord.RECORD_TYPE, signedPayload.getRecord(1).getRecordType());
            assertFalse("rec1=not marker", new SignatureRecord(signedPayload.getRecord(1)).isMarker());

        } catch (InvalidSignatureRecord ex) {
            ex.printStackTrace();
            fail("InvalidSignatureRecord: "+ex.getMessage());
        } catch (SignatureException ex) {
            ex.printStackTrace();
            fail("SignException: " + ex.getMessage());
        } catch (DataLengthException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
    }
    
    
    
    private void testSignAndValidate() throws AssertionFailedException {
        try {
            System.out.println("testSignAndValidate");

            URIRecord uri1 = new URIRecord(new byte[]{(byte)1}, "http://www.ericsson.com/1");
            URIRecord uri2 = new URIRecord(new byte[]{(byte)2}, "http://www.ericsson.com/2");

            NDEFRecord[] records = new NDEFRecord[]{uri1.toNDEFRecord(), uri2.toNDEFRecord()};
            NDEFMessage payload = new NDEFMessage(records);
            PrivateKey privKey = new PrivateKey(getClass().getResourceAsStream("files/KalleKarlsson-priv.der"));
            CertificateBytes[] chain = new CertificateBytes[] { 
                new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/KalleKarlsson.cer")).getBytes()), 
                new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/TrainCompanyCA.cer")).getBytes()), 
                new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer")).getBytes()),
                new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/SodermanlandCA.cer")).getBytes())
            };

            Signer signer = new RSASSAPSSSigner(privKey, CertificateField.X509, chain, null);
            MessageToSign toSign = new MessageToSign(payload);
            toSign.setSigner(new int[]{1}, signer);
            SignedMessage signed = toSign.signMessage();
            NDEFMessage signedPayload = signed.toNDEFMessage();

            NDEFRecord smartPosterRecord = new NDEFRecord(smartPosterRecordType, new byte[0], signedPayload.toByteArray());

            byte[] messageOnTag = smartPosterRecord.toByteArray();
//            Utils.dumpAsJavaByteArray(messageOnTag);

            X509Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream("files/SwedenRootCA.cer"))};
            NDEFMessage msg = new NDEFMessage(messageOnTag, 0);
            NDEFRecord r = msg.getRecord(0);

            System.out.println("record type: " + r.getRecordType().getName());
            
            MessageValidator validator = new MessageValidator(trustedCerts, null, new EmptyChainFetcher());
            SignedMessage result = validator.validate(r.getNestedNDEFMessage(0), new int[]{0, 2});

            Signature signature0 = result.getRecordsSignature(0);
            Signature signature2 = result.getRecordsSignature(2);

            assertNull("record 0 - not signed", signature0);
            assertNotNull("record 2 signed", signature2);
            assertTrue("record 1 - signed, but "+signature2.getStatus().getErrorMessage(), signature2.getStatus().isValid());
            
        } catch (SignatureException ex) {
            ex.printStackTrace();
            fail("SignException: " + ex.getMessage());
        } catch (DataLengthException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
    }
    
    
    
    /**
     * Tests that the CertificateFetcher is used
     */
    private void testChainFetcher() throws AssertionFailedException {
        System.out.println("testChainFetcher");
        try {

            // For fetcher to use
            final X509Certificate[] x509Certificates = new X509Certificate[]{ 
                new X509Certificate(getClass().getResourceAsStream("files/KalleKarlsson.cer")), 
                new X509Certificate(getClass().getResourceAsStream("files/TrainCompanyCA.cer")), 
                new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer")), 
                new X509Certificate(getClass().getResourceAsStream("files/SodermanlandCA.cer"))
            };
            
            class CallbackVariable {
                boolean called;
            }
            final CallbackVariable called = new CallbackVariable();
            
            final URIRecord certURI = new URIRecord("myprotocol:fetcher1");

            CertificateFetcher fetcher1 = new CertificateFetcher() {
                public CertificateChain fetchChain(URIRecord chainURI) {
                    try {
                        assertEquals("fetcher1 URI", certURI, chainURI);
                    } catch (AssertionFailedException ex) {
                        ex.printStackTrace();
                        throw new RuntimeException("Assertion failed: "+ex.getMessage());
                    }
                    
                    called.called = true;

                    return new X509CertificateChain(x509Certificates);
                }
            };

            
            //////////// Sign
            
            URIRecord uri1 = new URIRecord(new byte[]{(byte)1}, "http://www.ericsson.com/1");
            URIRecord uri2 = new URIRecord(new byte[]{(byte)2}, "http://www.ericsson.com/2");

            NDEFRecord[] records = new NDEFRecord[]{uri1.toNDEFRecord(), uri2.toNDEFRecord()};
            NDEFMessage payload = new NDEFMessage(records);
            PrivateKey privKey = new PrivateKey(getClass().getResourceAsStream("files/KalleKarlsson-priv.der"));
            
            Signer signer = new RSASSAPSSSigner(privKey, CertificateField.X509, new CertificateBytes[0], certURI);
            MessageToSign toSign = new MessageToSign(payload);
            toSign.setSigner(new int[]{1}, signer);
            SignedMessage signed = toSign.signMessage();
            NDEFMessage signedPayload = signed.toNDEFMessage();

            NDEFRecord smartPosterRecord = new NDEFRecord(smartPosterRecordType, new byte[0], signedPayload.toByteArray());

            NDEFMessage theMessage = new NDEFMessage(new NDEFRecord[]{smartPosterRecord});
                        
            
            //////////// Verify
            
            X509Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream("files/SwedenRootCA.cer"))};
            NDEFRecord r = theMessage.getRecord(0);

            System.out.println("record type: " + r.getRecordType().getName());
            
            MessageValidator validator = new MessageValidator(trustedCerts, null, fetcher1);
            SignedMessage result = validator.validate(r.getNestedNDEFMessage(0), new int[]{0, 2});

            assertTrue("fetcher1 called", called.called);
            
            Signature signature0 = result.getRecordsSignature(0);
            Signature signature2 = result.getRecordsSignature(2);

            assertNull("record 0 - not signed", signature0);
            assertNotNull("record 1 - signed", signature2);
            assertTrue("record 1 - signed, but "+signature2.getStatus().getErrorMessage(), signature2.getStatus().isValid());
            
        } catch (SignatureException ex) {
            ex.printStackTrace();
            fail("SignException: " + ex.getMessage());
        } catch (DataLengthException ex) {
            ex.printStackTrace();
            fail("DataLengthException: " + ex.getMessage());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail("IOException2: " + ex.getMessage());
        }
    }
    
    private void testSignatureFetcher() throws AssertionFailedException {
        System.out.println("testSignatureFetcher");
        try {

            // For fetcher to use
            final CertificateBytes[] chain = new CertificateBytes[]{ 
                new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/KalleKarlsson.cer")).getBytes()), 
                new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/TrainCompanyCA.cer")).getBytes()), 
                new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer")).getBytes()), 
                new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/SodermanlandCA.cer")).getBytes())
            };
            
            class CallbackVariable {
                boolean called;
            }
            final CallbackVariable called = new CallbackVariable();
            
            final URIRecord signURI = new URIRecord("myprotocol:signature/id=4711");
            

            class SignatureRepository {
                private byte[] signature;

                public byte[] getSignature() {
                    return signature;
                }

                public void setSignature(byte[] signature) {
                    this.signature = signature;
                }
            }
            final SignatureRepository repository = new SignatureRepository();
            
            SignatureFetcher fetcher1 = new SignatureFetcher() {

                public byte[] fetchSignature(URIRecord uri) {
                    try {
                        assertEquals("fetcher1 URI", signURI, uri);
                    } catch (AssertionFailedException ex) {
                        ex.printStackTrace();
                        throw new RuntimeException("Assertion failed: " + ex.getMessage());
                    }
                    called.called = true;
                    
                    return repository.getSignature();
                }
            };

            
            //////////// Sign
            
            URIRecord uri1 = new URIRecord(new byte[]{(byte)1}, "http://www.ericsson.com/1");
            URIRecord uri2 = new URIRecord(new byte[]{(byte)2}, "http://www.ericsson.com/2");

            NDEFRecord[] records = new NDEFRecord[]{uri1.toNDEFRecord(), uri2.toNDEFRecord()};
            NDEFMessage payload = new NDEFMessage(records);
            PrivateKey privKey = new PrivateKey(getClass().getResourceAsStream("files/KalleKarlsson-priv.der"));
            
            Signer signer = new RSASSAPSSSigner(privKey, CertificateField.X509, chain, null);
            MessageToSign toSign = new MessageToSign(payload);
            toSign.setSigner(1, signer);
            
            SignedMessage signed = toSign.signMessage();
            
            repository.setSignature(signed.getRecordsSignature(1).getBytes());
            assertNotNull("sigBytes", repository.getSignature());
            
            signed.getRecordsSignature(1).setURI(signURI);
            
            NDEFMessage signedPayload = signed.toNDEFMessage();
            
            SignatureRecord sr = new SignatureRecord(signedPayload.getRecord(3));
            assertNull("signature stripped", sr.getSignatureField().getSignature());
            assertEquals("sig uri set", signURI, sr.getSignatureField().getUri());

            NDEFRecord smartPosterRecord = new NDEFRecord(smartPosterRecordType, new byte[0], signedPayload.toByteArray());

            NDEFMessage theMessage = new NDEFMessage(new NDEFRecord[]{smartPosterRecord});
                        
            
            //////////// Verify
            
            X509Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream("files/SwedenRootCA.cer"))};
            NDEFRecord r = theMessage.getRecord(0);

            System.out.println("record type: " + r.getRecordType().getName());
            
            MessageValidator validator = new MessageValidator(trustedCerts, fetcher1, null);
            SignedMessage result = validator.validate(r.getNestedNDEFMessage(0), new int[]{0, 2});

            assertTrue("fetcher1 called", called.called);
            
            Signature signature0 = result.getRecordsSignature(0);
            Signature signature2 = result.getRecordsSignature(2);

            assertNull("record 0 - not signed", signature0);
            assertNotNull("record 1 - signed", signature2);
            assertTrue("record 1 - signed, but "+signature2.getStatus().getErrorMessage(), signature2.getStatus().isValid());
            
        } catch (InvalidSignatureRecord ex) {
            ex.printStackTrace();
            fail("InvalidSignatureRecord: " + ex.getMessage());
        } catch (SignatureException ex) {
            ex.printStackTrace();
            fail("SignException: " + ex.getMessage());
        } catch (DataLengthException ex) {
            ex.printStackTrace();
            fail("DataLengthException: " + ex.getMessage());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail("IOException2: " + ex.getMessage());
        }
    }
    
    public void testSignatureRanges() throws AssertionFailedException {
        try {
            System.out.println("testSignatureRanges");

            PrivateKey privKey = new PrivateKey(getClass().getResourceAsStream("files/KalleKarlsson-priv.der"));
            Signer signer = new RSASSAPSSSigner(privKey, CertificateField.X509, new CertificateBytes[0], new URIRecord("dummy:uri"));
            
            // 2 records
            NDEFMessage msg = new NDEFMessage(new NDEFRecord[]{new NDEFRecord(smartPosterRecordType, new byte[]{(byte)1}, (byte[])null), 
                                                                new NDEFRecord(smartPosterRecordType, new byte[]{(byte)2}, (byte[])null)});
            
            MessageToSign ranges = new MessageToSign(msg);
            ranges.setSigner(new int[]{1}, signer);
            
            SignatureRange[] theRanges = ranges.getSignatureRanges();
            
            assertEquals("numRanges", 1, theRanges.length);
            assertNotNull("1st element not null", theRanges[0]);
            
        } catch (IOException ex) {
            ex.printStackTrace();
            fail("IOException: " + ex.getMessage());
        }
    }

    private void testSignWithPSSExternal() throws AssertionFailedException {
        try {
            System.out.println("testSignWithPSSExternal");

            URIRecord uri1 = new URIRecord(new byte[]{(byte) 1}, "http://www.ericsson.com/1");
            URIRecord uri2 = new URIRecord(new byte[]{(byte) 2}, "http://www.ericsson.com/2");

            NDEFRecord[] records = new NDEFRecord[]{uri1.toNDEFRecord(), uri2.toNDEFRecord()};
            NDEFMessage payload = new NDEFMessage(records);
            PrivateKey privKey = new PrivateKey(getClass().getResourceAsStream("files/KalleKarlsson-priv.der"));
            CertificateBytes[] chain = new CertificateBytes[]{new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/KalleKarlsson.cer")).getBytes()), new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/TrainCompanyCA.cer")).getBytes()), new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer")).getBytes())};
            NDEFRecordType externalType = new NDEFRecordType(NDEFRecordType.EXTERNAL_RTD, "urn:nfc:ext:ericsson.com:ExternalSig1");

            Signer signer = new RSASSAPSSSigner(privKey, CertificateField.X509, chain, null, externalType);
            MessageToSign toSign = new MessageToSign(payload);
            toSign.setSigner(1, signer);
            SignedMessage signed = toSign.signMessage();
            NDEFMessage signedPayload = signed.toNDEFMessage();
            
            System.out.println("records: "+signedPayload.getNumberOfRecords());
            
            try {
                System.out.println("rec0: " + signedPayload.getRecord(0).getRecordType().getName());
                System.out.println("rec1: " + signedPayload.getRecord(1).getRecordType().getName());
                System.out.println("rec2: " + signedPayload.getRecord(2).getRecordType().getName());
                System.out.println("rec3: " + signedPayload.getRecord(3).getRecordType().getName());
                
                assertEquals("rec0=U", URIRecord.RECORD_TYPE, signedPayload.getRecord(0).getRecordType());
                assertEquals("rec1=ExtSig", externalType, signedPayload.getRecord(1).getRecordType());
                assertTrue("rec1=marker", new SignatureRecord(signedPayload.getRecord(1)).isMarker());
                assertEquals("rec2=U", URIRecord.RECORD_TYPE, signedPayload.getRecord(2).getRecordType());
                assertEquals("rec3=ExtSig", externalType, signedPayload.getRecord(3).getRecordType());
                assertFalse("rec3=not marker", new SignatureRecord(signedPayload.getRecord(3)).isMarker());
                
            } catch (Throwable ex) {
                ex.printStackTrace();
                fail("SigRec-fail:" + ex);
            }
            
        } catch (SignatureException ex) {
            ex.printStackTrace();
            fail("SignException: " + ex.getMessage());
        } catch (DataLengthException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
    }

    private void testSignAndValidateExternalType() throws AssertionFailedException {
        try {
            System.out.println("testSignAndValidateExternalType");

            URIRecord uri1 = new URIRecord(new byte[]{(byte)1}, "http://www.ericsson.com/1");
            URIRecord uri2 = new URIRecord(new byte[]{(byte)2}, "http://www.ericsson.com/2");
            final NDEFRecordType externalType = new NDEFRecordType(NDEFRecordType.EXTERNAL_RTD, "urn:nfc:ext:ericsson.com:ExternalSig1");

            NDEFRecord[] records = new NDEFRecord[]{uri1.toNDEFRecord(), uri2.toNDEFRecord()};
            NDEFMessage payload = new NDEFMessage(records);
            PrivateKey privKey = new PrivateKey(getClass().getResourceAsStream("files/KalleKarlsson-priv.der"));
            CertificateBytes[] chain = new CertificateBytes[] {
                new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/KalleKarlsson.cer")).getBytes()),
                new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/TrainCompanyCA.cer")).getBytes()),
                new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer")).getBytes()),
                new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/SodermanlandCA.cer")).getBytes())
            };

            Signer signer = new RSASSAPSSSigner(privKey, CertificateField.X509, chain, null, externalType);
            MessageToSign toSign = new MessageToSign(payload);
            toSign.setSigner(new int[]{1}, signer);
            SignedMessage signed = toSign.signMessage();
            NDEFMessage signedPayload = signed.toNDEFMessage();

            NDEFRecord smartPosterRecord = new NDEFRecord(smartPosterRecordType, new byte[0], signedPayload.toByteArray());

            byte[] messageOnTag = smartPosterRecord.toByteArray();
//            Utils.dumpAsJavaByteArray(messageOnTag);

            X509Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream("files/SwedenRootCA.cer"))};
            NDEFMessage msg = new NDEFMessage(messageOnTag, 0);
            NDEFRecord r = msg.getRecord(0);

            System.out.println("record type: " + r.getRecordType().getName());

            MessageValidator validator = new MessageValidator(trustedCerts, null, new EmptyChainFetcher()) {

                protected boolean isSignatureRecord(NDEFRecordType recordType) {
                    if(externalType.equals(recordType)) {
                        return true;
                    }
                    return super.isSignatureRecord(recordType);
                }

            };
            SignedMessage result = validator.validate(r.getNestedNDEFMessage(0), new int[]{0, 2});

            Signature signature0 = result.getRecordsSignature(0);
            Signature signature2 = result.getRecordsSignature(2);

            assertNull("record 0 - not signed", signature0);
            assertNotNull("record 2 signed", signature2);
            assertTrue("record 1 - signed, but "+signature2.getStatus().getErrorMessage(), signature2.getStatus().isValid());

        } catch (SignatureException ex) {
            ex.printStackTrace();
            fail("SignException: " + ex.getMessage());
        } catch (DataLengthException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
    }

    private void testSignAndValidateOtherSigAlg() throws AssertionFailedException {
        try {
            System.out.println("testSignAndValidateOtherSigAlg");

            URIRecord uri1 = new URIRecord(new byte[]{(byte)1}, "http://www.ericsson.com/1");
            URIRecord uri2 = new URIRecord(new byte[]{(byte)2}, "http://www.ericsson.com/2");
            final NDEFRecordType externalType = new NDEFRecordType(NDEFRecordType.EXTERNAL_RTD, "urn:nfc:ext:ericsson.com:SigExt5");

            NDEFRecord[] records = new NDEFRecord[]{uri1.toNDEFRecord(), uri2.toNDEFRecord()};
            NDEFMessage payload = new NDEFMessage(records);
            PrivateKey privKey = new PrivateKey(getClass().getResourceAsStream("files/KalleKarlsson-priv.der"));
            CertificateBytes[] chain = new CertificateBytes[] {
                new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/KalleKarlsson.cer")).getBytes()),
                new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/TrainCompanyCA.cer")).getBytes()),
                new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer")).getBytes()),
                new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/SodermanlandCA.cer")).getBytes())
            };

            Signer signer = new OtherAlgSigner(privKey, CertificateField.X509, chain, null, externalType);
            MessageToSign toSign = new MessageToSign(payload);
            toSign.setSigner(new int[]{1}, signer);
            SignedMessage signed = toSign.signMessage();
            NDEFMessage signedPayload = signed.toNDEFMessage();

            NDEFRecord smartPosterRecord = new NDEFRecord(smartPosterRecordType, new byte[0], signedPayload.toByteArray());

            byte[] messageOnTag = smartPosterRecord.toByteArray();
//            Utils.dumpAsJavaByteArray(messageOnTag);

            X509Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream("files/SwedenRootCA.cer"))};
            NDEFMessage msg = new NDEFMessage(messageOnTag, 0);
            NDEFRecord r = msg.getRecord(0);

            System.out.println("record type: " + r.getRecordType().getName());

            MessageValidator validator = new MessageValidator(trustedCerts, null, new EmptyChainFetcher()) {

                protected boolean isSignatureRecord(NDEFRecordType recordType) {
                    if(externalType.equals(recordType)) {
                        return true;
                    }
                    return super.isSignatureRecord(recordType);
                }

            };

            validator.setVerifierFactory(new DefaultVerifierFactory() {

                public SignatureVerifier getVerifier(NDEFRecordType signatureRecordType, int signatureType) {
                    if(externalType.equals(signatureRecordType) && signatureType == 0x05) {
                        return new OtherAlgVerifier();
                    }
                    return super.getVerifier(signatureRecordType, signatureType);
                }

            });

            SignedMessage result = validator.validate(r.getNestedNDEFMessage(0), new int[]{0, 2});

            Signature signature0 = result.getRecordsSignature(0);
            Signature signature2 = result.getRecordsSignature(2);

            assertNull("record 0 - not signed", signature0);
            assertNotNull("record 2 signed", signature2);
            assertTrue("record 1 - signed, but "+signature2.getStatus().getErrorMessage(), signature2.getStatus().isValid());

        } catch (SignatureException ex) {
            ex.printStackTrace();
            fail("SignException: " + ex.getMessage());
        } catch (DataLengthException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
    }

    private void testSignAndValidateOtherCertFormat() throws AssertionFailedException {
        try {
            System.out.println("testSignAndValidateOtherCertFormat");

            URIRecord uri1 = new URIRecord(new byte[]{(byte)1}, "http://www.ericsson.com/1");
            URIRecord uri2 = new URIRecord(new byte[]{(byte)2}, "http://www.ericsson.com/2");
            final NDEFRecordType externalType = new NDEFRecordType(NDEFRecordType.EXTERNAL_RTD, "urn:nfc:ext:ericsson.com:SigExt5");

            NDEFRecord[] records = new NDEFRecord[]{uri1.toNDEFRecord(), uri2.toNDEFRecord()};
            NDEFMessage payload = new NDEFMessage(records);
            PrivateKey privKey = new PrivateKey(getClass().getResourceAsStream("files/KalleKarlsson-priv.der"));
            CertificateBytes[] chain = new CertificateBytes[] {
                new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/KalleKarlsson.cer")).getBytes()),
                new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/TrainCompanyCA.cer")).getBytes()),
                new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer")).getBytes()),
                new CertificateBytes(new X509Certificate(getClass().getResourceAsStream("files/SodermanlandCA.cer")).getBytes())
            };

            Signer signer = new RSASSAPSSSigner(privKey, 0x02, chain, null, externalType);
            MessageToSign toSign = new MessageToSign(payload);
            toSign.setSigner(new int[]{1}, signer);
            SignedMessage signed = toSign.signMessage();
            NDEFMessage signedPayload = signed.toNDEFMessage();

            NDEFRecord smartPosterRecord = new NDEFRecord(smartPosterRecordType, new byte[0], signedPayload.toByteArray());

            byte[] messageOnTag = smartPosterRecord.toByteArray();
//            Utils.dumpAsJavaByteArray(messageOnTag);

            X509Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream("files/SwedenRootCA.cer"))};
            NDEFMessage msg = new NDEFMessage(messageOnTag, 0);
            NDEFRecord r = msg.getRecord(0);

            System.out.println("record type: " + r.getRecordType().getName());

            MessageValidator validator = new MessageValidator(trustedCerts, null, new EmptyChainFetcher()) {

                protected boolean isSignatureRecord(NDEFRecordType recordType) {
                    if(externalType.equals(recordType)) {
                        return true;
                    }
                    return super.isSignatureRecord(recordType);
                }

            };

            validator.setCertificateChainFactory(new DefaultCertificateChainFactory() {

                public CertificateChain createInstance(NDEFRecordType signatureRecordType, int certificateFormat, CertificateBytes[] chainBytes) throws IOException {
                    if(certificateFormat == 0x02) {
                        return new X509CertificateChain(chainBytes);
                    }
                    return super.createInstance(signatureRecordType, certificateFormat, chainBytes);
                }
            });

            SignedMessage result = validator.validate(r.getNestedNDEFMessage(0), new int[]{0, 2});

            Signature signature0 = result.getRecordsSignature(0);
            Signature signature2 = result.getRecordsSignature(2);

            assertNull("record 0 - not signed", signature0);
            assertNotNull("record 2 signed", signature2);
            assertTrue("record 1 - signed, but "+signature2.getStatus().getErrorMessage(), signature2.getStatus().isValid());

        } catch (SignatureException ex) {
            ex.printStackTrace();
            fail("SignException: " + ex.getMessage());
        } catch (DataLengthException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
    }

    private void testValidateFromData2() throws AssertionFailedException {
        try {
            System.out.println("testValidateFromData2");

            NDEFMessage message = new NDEFMessage(new NDEFRecord[]{new NDEFRecord(testData.getBytes(TestData.msgSpUrlAUser1), 0)});

            // Trusted certs
            X509Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream(Files.A_ROOT_CA))};

            // Find SmartPoster and extract the nested message
            NDEFRecord spRecord = message.getRecord(0);
            assertEquals("spRecord", smartPosterRecordType, spRecord.getRecordType());
            NDEFMessage sp = spRecord.getNestedNDEFMessage(0);

            // Do the validation
            MessageValidator validator = new MessageValidator(trustedCerts);
            SignedMessage signedMessage = validator.validate(sp);

            Signature signature = signedMessage.getRecordsSignature(0);
            assertNotNull("record 0 should be signed", signature);
            assertTrue("signature should be valid", signature.isValid());

            URIRecord uri = new URIRecord(sp.getRecord(0).getPayload(), 0);
            assertEquals("uri", "http://www.ericsson.com", uri.getURI());

            assertNull("record 1 should not be signed", signedMessage.getRecordsSignature(1));

        } catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
    }

    private void testValidateFromData3() throws AssertionFailedException {
        try {
            System.out.println("testValidateFromData3");

            NDEFMessage message = new NDEFMessage(new NDEFRecord[]{new NDEFRecord(testData.getBytes(TestData.msgSpUrlDUser2), 0)});

            // Trusted certs
            X509Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream(Files.D_ROOT_CA))};

            // Find SmartPoster and extract the nested message
            NDEFRecord spRecord = message.getRecord(0);
            assertEquals("spRecord", smartPosterRecordType, spRecord.getRecordType());
            NDEFMessage sp = spRecord.getNestedNDEFMessage(0);

            // Do the validation
            MessageValidator validator = new MessageValidator(trustedCerts);
            SignedMessage signedMessage = validator.validate(sp);

            Signature signature = signedMessage.getRecordsSignature(0);
            assertNotNull("record 0 should be signed", signature);
            assertTrue("signature should be valid, but:" + signature.getStatus().getErrorMessage(), signature.isValid());

            URIRecord uri = new URIRecord(sp.getRecord(0).getPayload(), 0);
            assertEquals("uri", "http://www.ericsson.com", uri.getURI());

            assertNull("record 1 should not be signed", signedMessage.getRecordsSignature(1));

        } catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
    }


}


